/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"mozilla/ArrayUtils.h"#include"mozilla/dom/SVGUseElement.h"#include"mozilla/dom/SVGUseElementBinding.h"#include"nsGkAtoms.h"#include"mozilla/dom/SVGSVGElement.h"#include"nsIDocument.h"#include"nsIPresShell.h"#include"mozilla/dom/Element.h"#include"nsContentUtils.h"#include"nsIURI.h"#include"mozilla/URLExtraData.h"#include"nsSVGEffects.h"NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use)namespacemozilla{namespacedom{JSObject*SVGUseElement::WrapNode(JSContext*aCx,JS::Handle<JSObject*>aGivenProto){returnSVGUseElementBinding::Wrap(aCx,this,aGivenProto);}////////////////////////////////////////////////////////////////////////// implementationnsSVGElement::LengthInfoSVGUseElement::sLengthInfo[4]={{&nsGkAtoms::x,0,nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER,SVGContentUtils::X},{&nsGkAtoms::y,0,nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER,SVGContentUtils::Y},{&nsGkAtoms::width,0,nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER,SVGContentUtils::X},{&nsGkAtoms::height,0,nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER,SVGContentUtils::Y},};nsSVGElement::StringInfoSVGUseElement::sStringInfo[2]={{&nsGkAtoms::href,kNameSpaceID_None,true},{&nsGkAtoms::href,kNameSpaceID_XLink,true}};//----------------------------------------------------------------------// nsISupports methodsNS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement)NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGUseElement,SVGUseElementBase)nsAutoScriptBlockerscriptBlocker;NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal)tmp->DestroyAnonymousContent();tmp->UnlinkSource();NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,SVGUseElementBase)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClone)tmp->mSource.Traverse(&cb);NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDNS_IMPL_ADDREF_INHERITED(SVGUseElement,SVGUseElementBase)NS_IMPL_RELEASE_INHERITED(SVGUseElement,SVGUseElementBase)NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGUseElement)NS_INTERFACE_TABLE_INHERITED(SVGUseElement,nsIMutationObserver)NS_INTERFACE_TABLE_TAIL_INHERITING(SVGUseElementBase)//----------------------------------------------------------------------// ImplementationSVGUseElement::SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>&aNodeInfo):SVGUseElementBase(aNodeInfo),mSource(this){}SVGUseElement::~SVGUseElement(){UnlinkSource();}//----------------------------------------------------------------------// nsIDOMNode methodsnsresultSVGUseElement::Clone(mozilla::dom::NodeInfo*aNodeInfo,nsINode**aResult,boolaPreallocateChildren)const{*aResult=nullptr;already_AddRefed<mozilla::dom::NodeInfo>ni=RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();SVGUseElement*it=newSVGUseElement(ni);nsCOMPtr<nsINode>kungFuDeathGrip(it);nsresultrv1=it->Init();nsresultrv2=const_cast<SVGUseElement*>(this)->CopyInnerTo(it,aPreallocateChildren);// SVGUseElement specific portion - record who we cloned fromit->mOriginal=const_cast<SVGUseElement*>(this);if(NS_SUCCEEDED(rv1)&&NS_SUCCEEDED(rv2)){kungFuDeathGrip.swap(*aResult);}returnNS_FAILED(rv1)?rv1:rv2;}already_AddRefed<SVGAnimatedString>SVGUseElement::Href(){returnmStringAttributes[HREF].IsExplicitlySet()?mStringAttributes[HREF].ToDOMAnimatedString(this):mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);}//----------------------------------------------------------------------already_AddRefed<SVGAnimatedLength>SVGUseElement::X(){returnmLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);}already_AddRefed<SVGAnimatedLength>SVGUseElement::Y(){returnmLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);}already_AddRefed<SVGAnimatedLength>SVGUseElement::Width(){returnmLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);}already_AddRefed<SVGAnimatedLength>SVGUseElement::Height(){returnmLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);}//----------------------------------------------------------------------// nsIMutationObserver methodsvoidSVGUseElement::CharacterDataChanged(nsIDocument*aDocument,nsIContent*aContent,CharacterDataChangeInfo*aInfo){if(nsContentUtils::IsInSameAnonymousTree(this,aContent)){TriggerReclone();}}voidSVGUseElement::AttributeChanged(nsIDocument*aDocument,Element*aElement,int32_taNameSpaceID,nsIAtom*aAttribute,int32_taModType,constnsAttrValue*aOldValue){if(nsContentUtils::IsInSameAnonymousTree(this,aElement)){TriggerReclone();}}voidSVGUseElement::ContentAppended(nsIDocument*aDocument,nsIContent*aContainer,nsIContent*aFirstNewContent,int32_taNewIndexInContainer){if(nsContentUtils::IsInSameAnonymousTree(this,aContainer)){TriggerReclone();}}voidSVGUseElement::ContentInserted(nsIDocument*aDocument,nsIContent*aContainer,nsIContent*aChild,int32_taIndexInContainer){if(nsContentUtils::IsInSameAnonymousTree(this,aChild)){TriggerReclone();}}voidSVGUseElement::ContentRemoved(nsIDocument*aDocument,nsIContent*aContainer,nsIContent*aChild,int32_taIndexInContainer,nsIContent*aPreviousSibling){if(nsContentUtils::IsInSameAnonymousTree(this,aChild)){TriggerReclone();}}voidSVGUseElement::NodeWillBeDestroyed(constnsINode*aNode){nsCOMPtr<nsIMutationObserver>kungFuDeathGrip(this);UnlinkSource();}//----------------------------------------------------------------------nsIContent*SVGUseElement::CreateAnonymousContent(){mClone=nullptr;if(mSource.get()){mSource.get()->RemoveMutationObserver(this);}LookupHref();nsIContent*targetContent=mSource.get();if(!targetContent)returnnullptr;// make sure target is valid type for <use>// QIable nsSVGGraphicsElement would eliminate enumerating all elementsif(!targetContent->IsAnyOfSVGElements(nsGkAtoms::svg,nsGkAtoms::symbol,nsGkAtoms::g,nsGkAtoms::path,nsGkAtoms::text,nsGkAtoms::rect,nsGkAtoms::circle,nsGkAtoms::ellipse,nsGkAtoms::line,nsGkAtoms::polyline,nsGkAtoms::polygon,nsGkAtoms::image,nsGkAtoms::use))returnnullptr;// circular loop detection// check 1 - check if we're a document descendent of the targetif(nsContentUtils::ContentIsDescendantOf(this,targetContent))returnnullptr;// check 2 - check if we're a clone, and if we already exist in the hierarchyif(GetParent()&&mOriginal){for(nsCOMPtr<nsIContent>content=GetParent();content;content=content->GetParent()){if(content->IsSVGElement(nsGkAtoms::use)&&static_cast<SVGUseElement*>(content.get())->mOriginal==mOriginal){returnnullptr;}}}nsCOMPtr<nsINode>newnode;nsNodeInfoManager*nodeInfoManager=targetContent->OwnerDoc()==OwnerDoc()?nullptr:OwnerDoc()->NodeInfoManager();nsNodeUtils::Clone(targetContent,true,nodeInfoManager,nullptr,getter_AddRefs(newnode));nsCOMPtr<nsIContent>newcontent=do_QueryInterface(newnode);if(!newcontent)returnnullptr;if(newcontent->IsAnyOfSVGElements(nsGkAtoms::svg,nsGkAtoms::symbol)){nsSVGElement*newElement=static_cast<nsSVGElement*>(newcontent.get());if(mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())newElement->SetLength(nsGkAtoms::width,mLengthAttributes[ATTR_WIDTH]);if(mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())newElement->SetLength(nsGkAtoms::height,mLengthAttributes[ATTR_HEIGHT]);}// Store the base URInsCOMPtr<nsIURI>baseURI=targetContent->GetBaseURI();if(!baseURI){returnnullptr;}mContentURLData=newURLExtraData(baseURI.forget(),do_AddRef(OwnerDoc()->GetDocumentURI()),do_AddRef(NodePrincipal()));targetContent->AddMutationObserver(this);mClone=newcontent;#ifdef DEBUG// Our anonymous clone can get restyled by various things// (e.g. SMIL). Reconstructing its frame is OK, though, because// it's going to be our _only_ child in the frame tree, so can't get// mis-ordered with anything.mClone->SetProperty(nsGkAtoms::restylableAnonymousNode,reinterpret_cast<void*>(true));#endif // DEBUGreturnmClone;}nsIURI*SVGUseElement::GetSourceDocURI(){nsIContent*targetContent=mSource.get();if(!targetContent)returnnullptr;returntargetContent->OwnerDoc()->GetDocumentURI();}voidSVGUseElement::DestroyAnonymousContent(){nsContentUtils::DestroyAnonymousContent(&mClone);}boolSVGUseElement::OurWidthAndHeightAreUsed()const{returnmClone&&mClone->IsAnyOfSVGElements(nsGkAtoms::svg,nsGkAtoms::symbol);}//----------------------------------------------------------------------// implementation helpersvoidSVGUseElement::SyncWidthOrHeight(nsIAtom*aName){NS_ASSERTION(aName==nsGkAtoms::width||aName==nsGkAtoms::height,"The clue is in the function name");NS_ASSERTION(OurWidthAndHeightAreUsed(),"Don't call this");if(OurWidthAndHeightAreUsed()){nsSVGElement*target=static_cast<nsSVGElement*>(mClone.get());uint32_tindex=*sLengthInfo[ATTR_WIDTH].mName==aName?ATTR_WIDTH:ATTR_HEIGHT;if(mLengthAttributes[index].IsExplicitlySet()){target->SetLength(aName,mLengthAttributes[index]);return;}if(mClone->IsSVGElement(nsGkAtoms::svg)){// Our width/height attribute is now no longer explicitly set, so we// need to revert the clone's width/height to the width/height of the// content that's being cloned.TriggerReclone();return;}// Our width/height attribute is now no longer explicitly set, so we// need to set the value to 100%nsSVGLength2length;length.Init(SVGContentUtils::XY,0xff,100,nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);target->SetLength(aName,length);return;}}voidSVGUseElement::LookupHref(){nsAutoStringhref;if(mStringAttributes[HREF].IsExplicitlySet()){mStringAttributes[HREF].GetAnimValue(href,this);}else{mStringAttributes[XLINK_HREF].GetAnimValue(href,this);}if(href.IsEmpty()){return;}nsCOMPtr<nsIURI>originURI=mOriginal?mOriginal->GetBaseURI():GetBaseURI();nsCOMPtr<nsIURI>baseURI=nsContentUtils::IsLocalRefURL(href)?nsSVGEffects::GetBaseURLForLocalRef(this,originURI):originURI;nsCOMPtr<nsIURI>targetURI;nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),href,GetComposedDoc(),baseURI);mSource.Reset(this,targetURI);}voidSVGUseElement::TriggerReclone(){nsIDocument*doc=GetComposedDoc();if(!doc)return;nsIPresShell*presShell=doc->GetShell();if(!presShell)return;presShell->PostRecreateFramesFor(this);}voidSVGUseElement::UnlinkSource(){if(mSource.get()){mSource.get()->RemoveMutationObserver(this);}mSource.Unlink();}//----------------------------------------------------------------------// nsSVGElement methods/* virtual */gfxMatrixSVGUseElement::PrependLocalTransformsTo(constgfxMatrix&aMatrix,SVGTransformTypesaWhich)const{// 'transform' attribute:gfxMatrixuserToParent;if(aWhich==eUserSpaceToParent||aWhich==eAllTransforms){userToParent=GetUserToParentTransform(mAnimateMotionTransform,mTransforms);if(aWhich==eUserSpaceToParent){returnuserToParent*aMatrix;}}// our 'x' and 'y' attributes:floatx,y;const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x,&y,nullptr);gfxMatrixchildToUser=gfxMatrix::Translation(x,y);if(aWhich==eAllTransforms){returnchildToUser*userToParent*aMatrix;}MOZ_ASSERT(aWhich==eChildToUserSpace,"Unknown TransformTypes");// The following may look broken because pre-multiplying our eChildToUserSpace// transform with another matrix without including our eUserSpaceToParent// transform between the two wouldn't make sense. We don't expect that to// ever happen though. We get here either when the identity matrix has been// passed because our caller just wants our eChildToUserSpace transform, or// when our eUserSpaceToParent transform has already been multiplied into the// matrix that our caller passes (such as when we're called from PaintSVG).returnchildToUser*aMatrix;}/* virtual */boolSVGUseElement::HasValidDimensions()const{return(!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet()||mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits()>0)&&(!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet()||mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits()>0);}nsSVGElement::LengthAttributesInfoSVGUseElement::GetLengthInfo(){returnLengthAttributesInfo(mLengthAttributes,sLengthInfo,ArrayLength(sLengthInfo));}nsSVGElement::StringAttributesInfoSVGUseElement::GetStringInfo(){returnStringAttributesInfo(mStringAttributes,sStringInfo,ArrayLength(sStringInfo));}//----------------------------------------------------------------------// nsIContent methodsNS_IMETHODIMP_(bool)SVGUseElement::IsAttributeMapped(constnsIAtom*name)const{staticconstMappedAttributeEntry*constmap[]={sFEFloodMap,sFiltersMap,sFontSpecificationMap,sGradientStopMap,sLightingEffectsMap,sMarkersMap,sTextContentElementsMap,sViewportsMap};returnFindAttributeDependence(name,map)||SVGUseElementBase::IsAttributeMapped(name);}}// namespace dom}// namespace mozilla